#pragma once
#include "Utility.h"
#include <cstddef>
#include <utility>
#include <type_traits>
#include <vector>
#include <memory>
#include <typeinfo>

#define __METHOD_IMPL(TL, PL, RL, CM, X0, X1, X2, X3) \
    template< typename TReturn CM RL(_CLASS_TYPE) > \
    class Method< TReturn (RL(_VAR_TYPE)) > \
    { \
    public: \
        virtual TReturn Invoke(RL(_TYPE_ARG)) const = 0; \
    public: \
        virtual void *Target() = 0; \
    public: \
        virtual void const *Target() const = 0; \
    public: \
        virtual std::type_info const &TargetTypeInfo() const = 0; \
    public: \
        virtual bool Equals(Method const &other) const throw () = 0; \
    };

#define __POINTER_METHOD_IMPL(TL, PL, RL, CM, X0, X1, X2, X3) \
    template< typename TReturn CM RL(_CLASS_TYPE) >  \
    class PointerMethod< TReturn X0 (RL(_VAR_TYPE)) > \
        : public Method< TReturn (RL(_VAR_TYPE)) > \
    { \
    public: \
        typedef typename TypeDef< TReturn X0 (RL(_VAR_TYPE)) >::Type MethodType; \
        typedef typename std::add_pointer<MethodType>::type MethodPointerType; \
        PointerMethod(MethodPointerType method) throw () \
            : _method(std::forward<MethodPointerType>(method)) \
            , _typeInfo(typeid (std::forward<MethodPointerType>(method))) { } \
        virtual TReturn Invoke(RL(_TYPE_ARG)) const override { return (*const_cast<MethodPointerType &>(_method))(RL(_FORWARD_ARG)); } \
        virtual void *Target() override { return std::addressof(_method); } \
        virtual void const *Target() const override { return std::addressof(_method); } \
        virtual std::type_info const &TargetTypeInfo() const override { return _typeInfo; } \
        virtual bool Equals(Method const &other) const throw () override \
        { \
            auto o = Utility::As<PointerMethod const>(&other); \
            return o == nullptr ? false : _method == o->_method; \
        } \
    private: \
        MethodPointerType _method; \
        std::type_info const &_typeInfo; \
    };

#define __OBJECT_METHOD_VALUE_IMPL(TL, PL, RL, CM, X0, X1, X2, X3) \
    template< typename TReturn CM RL(_CLASS_TYPE), typename U >  \
    class ObjectMethod< TReturn (RL(_VAR_TYPE)), U > \
        : public Method< TReturn (RL(_VAR_TYPE)) > \
    { \
    public: \
        typedef typename TypeDef< TReturn (RL(_VAR_TYPE)) >::Type MethodType; \
        ObjectMethod(U method) throw () : _method(std::forward<U>(method)), _typeInfo(typeid (std::forward<U>(method))) { } \
        virtual TReturn Invoke(RL(_TYPE_ARG))  const override { return const_cast<U &>(_method)(RL(_FORWARD_ARG)); } \
        virtual void *Target() override { return std::addressof(_method); } \
        virtual void const *Target() const override { return std::addressof(_method); } \
        virtual std::type_info const &TargetTypeInfo() const override { return _typeInfo; } \
        virtual bool Equals(Method const &other) const throw () override \
        { \
            auto o = Utility::As<ObjectMethod const>(&other); \
            return o == nullptr ? false : (Utility::IsAddressEqual(_method, o->_method) || (typeid (_method) == typeid (o->_method))); \
        } \
    private: \
        U _method; \
        std::type_info const &_typeInfo; \
    };

#define __OBJECT_METHOD_REF_IMPL(TL, PL, RL, CM, X0, X1, X2, X3) \
    template< typename TReturn CM RL(_CLASS_TYPE), typename U >  \
    class ObjectMethod< TReturn (RL(_VAR_TYPE)), U & > \
        : public Method< TReturn (RL(_VAR_TYPE)) > \
    { \
    public: \
        typedef typename TypeDef< TReturn (RL(_VAR_TYPE)) >::Type MethodType; \
        ObjectMethod(U &method) throw () : _method(std::forward<U &>(method)), _typeInfo(typeid (std::forward<U &>(method))) { } \
        virtual TReturn Invoke(RL(_TYPE_ARG))  const override { return const_cast<U &>(_method)(RL(_FORWARD_ARG)); } \
        virtual void *Target() override { return std::addressof(_method); } \
        virtual void const *Target() const override { return std::addressof(_method); } \
        virtual std::type_info const &TargetTypeInfo() const override { return _typeInfo; } \
        virtual bool Equals(Method const &other) const throw () override \
        { \
            auto o = Utility::As<ObjectMethod const>(&other); \
            return o == nullptr ? false : (Utility::IsAddressEqual(_method, o->_method) || (typeid (_method) == typeid ((o->_method)))); \
        } \
    private: \
        U &_method; \
        std::type_info const &_typeInfo; \
    };

#define __OBJECT_METHOD_PTR_IMPL(TL, PL, RL, CM, X0, X1, X2, X3) \
    template< typename TReturn CM RL(_CLASS_TYPE), typename U >  \
    class ObjectMethod< TReturn (RL(_VAR_TYPE)), U * > \
        : public Method< TReturn (RL(_VAR_TYPE)) > \
    { \
    public: \
        typedef typename TypeDef< TReturn (RL(_VAR_TYPE)) >::Type MethodType; \
        ObjectMethod(U *method) throw () : _method(std::forward<U *>(method)), _typeInfo(typeid (std::forward<U *>(method))) { } \
        virtual TReturn Invoke(RL(_TYPE_ARG))  const override { return (*const_cast<U *&>(_method))(RL(_FORWARD_ARG)); } \
        virtual void *Target() override { return std::addressof(_method); } \
        virtual void const *Target() const override { return std::addressof(_method); } \
        virtual std::type_info const &TargetTypeInfo() const override { return _typeInfo; } \
        virtual bool Equals(Method const &other) const throw () override \
        { \
            auto o = Utility::As<ObjectMethod const>(&other); \
            return o == nullptr ? false : ((_method == o->_method) || (typeid (*_method) == typeid (*(o->_method)))); \
        } \
    private: \
        U *_method; \
        std::type_info const &_typeInfo; \
    };

#define __MEMBER_METHOD_VALUE_IMPL(TL, PL, RL, CM, X0, X1, X2, X3) \
    template< typename TReturn, typename TClass CM RL(_CLASS_TYPE), typename U > \
    class MemberMethod< TReturn (TClass::*)(RL(_VAR_TYPE)) X0, U > \
        : public Method< TReturn (RL(_VAR_TYPE)) > \
    { \
    public: \
        typedef typename TypeDef< TReturn (TClass::*)(RL(_VAR_TYPE)) X0 >::Type MethodType; \
        MemberMethod(MethodType method, U object) throw () \
            : _method(std::forward<MethodType>(method)) \
            , _object(std::forward<U>(object)) \
            , _typeInfo(typeid (std::forward<MethodType>(method))) { } \
        virtual TReturn Invoke(RL(_TYPE_ARG)) const override { return (const_cast<U &>(_object).*(_method))(RL(_FORWARD_ARG)); } \
        virtual void *Target() override { return std::addressof(_method); } \
        virtual void const *Target() const override { return std::addressof(_method); } \
        virtual std::type_info const &TargetTypeInfo() const override { return _typeInfo; } \
        virtual bool Equals(Method const &other) const throw () override \
        { \
            auto o = Utility::As<MemberMethod const>(&other); \
            return o == nullptr ? false : _method == o->_method && Utility::IsAddressEqual(_object, o->_object); \
        } \
    private: \
        MethodType _method; \
        U _object; \
        std::type_info const &_typeInfo; \
    };

#define __MEMBER_METHOD_REF_IMPL(TL, PL, RL, CM, X0, X1, X2, X3) \
    template< typename TReturn, typename TClass CM RL(_CLASS_TYPE), typename U > \
    class MemberMethod< TReturn (TClass::*)(RL(_VAR_TYPE)) X0, U & > \
        : public Method< TReturn (RL(_VAR_TYPE)) > \
    { \
    public: \
        typedef typename TypeDef< TReturn (TClass::*)(RL(_VAR_TYPE)) X0 >::Type MethodType; \
        MemberMethod(MethodType method, U &object) throw () \
            : _method(std::forward<MethodType>(method)) \
            , _object(std::forward<U &>(object)) \
            , _typeInfo(typeid (std::forward<MethodType>(method))) { } \
        virtual TReturn Invoke(RL(_TYPE_ARG)) const override { return (const_cast<U &>(_object).*(_method))(RL(_FORWARD_ARG)); } \
        virtual void *Target() override { return std::addressof(_method); } \
        virtual void const *Target() const override { return std::addressof(_method); } \
        virtual std::type_info const &TargetTypeInfo() const override { return _typeInfo; } \
        virtual bool Equals(Method const &other) const throw () override \
        { \
            auto o = Utility::As<MemberMethod const>(&other); \
            return o == nullptr ? false : _method == o->_method && Utility::IsAddressEqual(_object, o->_object); \
        } \
    private: \
        MethodType _method; \
        U &_object; \
        std::type_info const &_typeInfo; \
    };

#define __MEMBER_METHOD_PTR_IMPL(TL, PL, RL, CM, X0, X1, X2, X3) \
    template< typename TReturn, typename TClass CM RL(_CLASS_TYPE), typename U > \
    class MemberMethod< TReturn (TClass::*)(RL(_VAR_TYPE)) X0, U * > \
        : public Method< TReturn (RL(_VAR_TYPE)) > \
    { \
    public: \
        typedef typename TypeDef< TReturn (TClass::*)(RL(_VAR_TYPE)) X0 >::Type MethodType; \
        MemberMethod(MethodType method, U *object) throw () \
            : _method(std::forward<MethodType>(method)) \
            , _object(std::forward<U *>(object)) \
            , _typeInfo(typeid (std::forward<MethodType>(method))) { } \
        virtual TReturn Invoke(RL(_TYPE_ARG)) const override { return ((*const_cast<U *&>(_object)).*(_method))(RL(_FORWARD_ARG)); } \
        virtual void *Target() override { return std::addressof(_method); } \
        virtual void const *Target() const override { return std::addressof(_method); } \
        virtual std::type_info const &TargetTypeInfo() const override { return _typeInfo; } \
        virtual bool Equals(Method const &other) const throw () override \
        { \
            auto o = Utility::As<MemberMethod const>(&other); \
            return o == nullptr ? false : _method == o->_method && _object == o->_object; \
        } \
    private: \
        MethodType _method; \
        U *_object; \
        std::type_info const &_typeInfo; \
    };

#define __DELEGATE_BASE_IMPL(TL, PL, RL, CM, X0, X1, X2, X3) \
    template< typename TReturn CM RL(_CLASS_TYPE) > \
    class DelegateBase< TReturn X0 (RL(_VAR_TYPE)) > \
    { \
    public: \
        typedef typename TypeDef< TReturn X0 (RL(_VAR_TYPE)) >::Type MethodType; \
        typedef TReturn ReturnType; \
        typedef Method< TReturn (RL(_VAR_TYPE))> MethodBase; \
        typedef std::vector< std::shared_ptr<MethodBase> > MethodContainer; \
        ReturnType Invoke(RL(_TYPE_ARG)) const \
        { \
            for (size_t i = 0; i < _methods.size() - 1; ++i) \
            { \
                _methods[i]->Invoke(RL(_FORWARD_ARG)); \
            } \
            return _methods[_methods.size() - 1]->Invoke(RL(_FORWARD_ARG)); \
        } \
        ReturnType operator () (RL(_TYPE_ARG)) const \
        { \
            return Invoke(RL(_FORWARD_ARG)); \
        } \
        DelegateBase() {} \
        template<typename T> \
        DelegateBase(T method) \
            : _methods() \
        { \
            typedef typename std::conditional< \
                    std::is_class<T>::value,  \
                    ObjectMethod< TReturn (RL(_VAR_TYPE)), T >,  \
                    PointerMethod< typename std::remove_pointer<T>::type > \
                >::type MethodCatagory; \
            _methods.push_back(std::make_shared<MethodCatagory>(std::forward<T>(method))); \
        } \
        template<typename T, typename U> \
        DelegateBase(T method, U object) \
            : _methods() \
        { \
            typedef MemberMethod<T, U> MethodCatagory; \
            _methods.push_back(std::make_shared<MethodCatagory>(std::forward<T>(method), std::forward<U>(object))); \
        } \
        DelegateBase(DelegateBase &&other) : _methods(std::move(other._methods)) { } \
        DelegateBase &operator = (DelegateBase &&other) \
        { \
            if (this != &other) \
            { \
                _methods = std::move(other._methods); \
            } \
            return *this; \
        } \
        virtual bool Equals(DelegateBase const &other) const throw () \
        { \
            auto o = &other; \
            if (_methods.size() != o->_methods.size()) \
            { \
                return false; \
            } \
            for (size_t i = 0; i < _methods.size(); ++i) \
            { \
                if (!_methods[i]->Equals(*o->_methods[i])) \
                { \
                    return false; \
                } \
            } \
            return true; \
        } \
        void AddHandle(DelegateBase const &delegate) \
        { \
            for (size_t i = 0; i < delegate._methods.size(); ++i) \
            { \
                _methods.push_back(delegate._methods[i]); \
            } \
        } \
        void operator += (DelegateBase const &delegate) \
        { \
            AddHandle(delegate); \
        } \
        bool RemoveHandle(DelegateBase const &delegate) \
        { \
            if (_methods.size() <= 1 || _methods.size() <= delegate._methods.size()) \
            { \
                return false; \
            } \
            for (size_t i = _methods.size() - delegate._methods.size() + 1; i > 0; --i) \
            { \
                size_t j; \
                for (j = 0; j < delegate._methods.size(); ++j) \
                { \
                    if (!_methods.[i - 1 + j]->Equals(delegate._methods[j])) \
                    { \
                        break; \
                    } \
                } \
                if (j >= delegate._methods.size()) \
                { \
                    for (--i; i < _methods.size() - delegate._methods.size(); ++i) \
                    { \
                        _methods[i] = _methods[i + delegate._methods.size()]; \
                    } \
                    _method.erase(_method.begin() + i, _method.end()); \
                    return true; \
                } \
            } \
            return false; \
        } \
        bool operator -= (DelegateBase const &delegate) \
        { \
            RemoveHandle(delegate); \
        } \
        size_t Count() const throw () \
        { \
            return _methods.size(); \
        } \
        bool IsEmpty() const throw () \
        { \
            return _methods.size() == 0; \
        } \
        void Clear() \
        { \
            _methods.clear(); \
        } \
        template<typename T> \
        T *Target(size_t index) \
        { \
            return TargetTypeInfo(index) == typeid (T) \
                ? static_cast<T *>(_methods[index]->Target()) \
                : nullptr; \
        } \
        template<typename T> \
        T const *Target(size_t index) const \
        { \
            return TargetTypeInfo(index) == typeid (T) \
                ? static_cast<T const *>(_methods[index]->Target()) \
                : nullptr; \
        } \
        std::type_info const &TargetTypeInfo(size_t index) const \
        { \
            return _methods[index]->TargetTypeInfo(); \
        } \
    private: \
        typename MethodContainer _methods; \
    };

#define __DELEGATE_IMPL(TL, PL, RL, CM, X0, X1, X2, X3) \
    template< typename TReturn CM RL(_CLASS_TYPE) > \
    class Delegate< TReturn X0 (RL(_VAR_TYPE)) > \
        : public DelegateBase< TReturn X0 (RL(_VAR_TYPE)) > \
    { \
    public: \
        Delegate() : DelegateBase() { } \
        template<typename T> \
        Delegate(T method) \
            : DelegateBase(std::forward<T>(method)) \
        { \
        } \
        template<typename T, typename U> \
        Delegate(T method, U object) \
            : DelegateBase(std::forward<T>(method), std::forward<U>(object)) \
        { \
        } \
        Delegate(Delegate &&other) \
            : DelegateBase(std::forward<DelegateBase &&>(other)) \
        { \
        } \
        Delegate &operator = (Delegate &&other) \
        { \
            if (this != &other) \
            { \
                *Utility::As<DelegateBase>(this) = std::forward<DelegateBase &&>(other); \
            } \
            return *this; \
        } \
    };

 #if defined(_M_CEE)
    #define _VARIADIC_EXPAND_0X_WITH_CALL_OPTION(FUNC, X1, X2, X3) \
        _VARIADIC_EXPAND_0X(FUNC, __cdecl, , , ); \
        _VARIADIC_EXPAND_0X(FUNC, __stdcall, , , ); \
        _VARIADIC_EXPAND_0X(FUNC, __clrcall, , , ) 
#else
    #define _VARIADIC_EXPAND_0X_WITH_CALL_OPTION(FUNC, X1, X2, X3) \
        _VARIADIC_EXPAND_0X(FUNC, __cdecl, , , ); \
        _VARIADIC_EXPAND_0X(FUNC, __stdcall, , , ); \
        _VARIADIC_EXPAND_0X(FUNC, __fastcall, , , ) 
#endif

namespace NsClasses
{
    template<typename T>
    struct TypeDef
    {
        typedef T Type;
    };

    template<typename T>
    class Method;
    _VARIADIC_EXPAND_0X(__METHOD_IMPL, , , , );

    template<typename T>
    class PointerMethod;
    _VARIADIC_EXPAND_0X_WITH_CALL_OPTION(__POINTER_METHOD_IMPL, , , );

    template<typename T0, typename T1>
    class ObjectMethod;
    _VARIADIC_EXPAND_0X(__OBJECT_METHOD_VALUE_IMPL, , , , );
    _VARIADIC_EXPAND_0X(__OBJECT_METHOD_REF_IMPL, , , , );
    _VARIADIC_EXPAND_0X(__OBJECT_METHOD_PTR_IMPL, , , , );
    
    template<typename T0, typename T1>
    class MemberMethod;
    _VARIADIC_EXPAND_0X(__MEMBER_METHOD_VALUE_IMPL, , , , );
    _VARIADIC_EXPAND_0X(__MEMBER_METHOD_VALUE_IMPL, const, , , );
    _VARIADIC_EXPAND_0X(__MEMBER_METHOD_VALUE_IMPL, volatile, , , );
    _VARIADIC_EXPAND_0X(__MEMBER_METHOD_VALUE_IMPL, const volatile, , , );

    _VARIADIC_EXPAND_0X(__MEMBER_METHOD_REF_IMPL, , , , );
    _VARIADIC_EXPAND_0X(__MEMBER_METHOD_REF_IMPL, const, , , );
    _VARIADIC_EXPAND_0X(__MEMBER_METHOD_REF_IMPL, volatile, , , );
    _VARIADIC_EXPAND_0X(__MEMBER_METHOD_REF_IMPL, const volatile, , , );

    _VARIADIC_EXPAND_0X(__MEMBER_METHOD_PTR_IMPL, , , , );
    _VARIADIC_EXPAND_0X(__MEMBER_METHOD_PTR_IMPL, const, , , );
    _VARIADIC_EXPAND_0X(__MEMBER_METHOD_PTR_IMPL, volatile, , , );
    _VARIADIC_EXPAND_0X(__MEMBER_METHOD_PTR_IMPL, const volatile, , , );

    template<typename T>
    class DelegateBase;
    _VARIADIC_EXPAND_0X_WITH_CALL_OPTION(__DELEGATE_BASE_IMPL, , , );

    template<typename T>
    class Delegate;
    _VARIADIC_EXPAND_0X_WITH_CALL_OPTION(__DELEGATE_IMPL, , , );
}

#undef __METHOD_IMPL
#undef __POINTER_METHOD_IMPL
#undef __OBJECT_METHOD_VALUE_IMPL
#undef __OBJECT_METHOD_PTR_IMPL
#undef __MEMBER_METHOD_VALUE_IMPL
#undef __MEMBER_METHOD_REF_IMPL
#undef __MEMBER_METHOD_PTR_IMPL
#undef __DELEGATE_BASE_IMPL
#undef __DELEGATE_IMPL